﻿// ReSharper disable CheckNamespace
namespace System.Threading.Tasks
// ReSharper restore CheckNamespace
{
    /// <summary>
    /// Extends TaskFactory to create tasks that support progress notification and cancellation.
    /// </summary>
    public static class TaskFactoryEx
    {
        /// <summary>
        /// Start a task that supports reporting results back in chunks including cancellation and progress notifications.
        /// </summary>
        /// <example><code>
        /// var tokenSource = new CancellationTokenSource();
        /// var resultTask = Task.Factory.StartNewWithProgress&lt;int[]&gt;((pn) =>
        /// {
        ///    var result = new List&lt;int&gt;();
        ///    for (int i = 0; i &lt; 100000; i++)
        ///    {
        ///        ...
        ///        result.Add(i);
        ///        pn.CancellationToken.ThrowIfCancellationRequested();
        ///        pn.Notify(new int[] { i }, i * 10);
        ///    }
        ///    return result.ToArray();
        /// },
        ///    // Progress Handling
        /// (chunk, progressPercentage) =>
        /// {
        ///    var threadID = Thread.CurrentThread.ManagedThreadId;
        ///    Console.WriteLine("{0} - {2} - {1}", progressPercentage, string.Join&lt;int&gt;(", ", chunk), threadID);
        ///    _threadOfExecution.Add(threadID);
        /// }, tokenSource.Token);
        /// </code>
        /// </example>
        /// <typeparam name="TResult">result type</typeparam>
        /// <param name="factory"><see cref="TaskFactory"/> instance to be extended.</param>
        /// <param name="function">function to execute async</param>
        /// <param name="progressHandler">handler that gets called on the caller's thread (e.g. UI thread) and where progress can be reported back to the 
        /// user.</param>
        /// <param name="cancellationToken">token that can be used inside the async function to abort the operation.</param>
        /// <returns><see cref="Task"/> that can be awaited</returns>
        public static Task<TResult> StartNewWithProgress<TResult>(this TaskFactory factory,
                                                                       Func<IProgressNotifier<TResult>, TResult> function,
                                                                       Action<TResult, int> progressHandler,
                                                                       CancellationToken cancellationToken)
        {
            var notifier = new ProgressNotifier<TResult>(cancellationToken)
                               {
                                   ProgressHandler = progressHandler
                               };
            return factory.StartNew((p) => function(notifier), cancellationToken);
        }

        /// <summary>
        /// Start a task that supports supports cancellation and progress notifications.
        /// </summary>
        /// <example><code>
        /// var tokenSource = new CancellationTokenSource();
        /// var resultTask = Task.Factory.StartNewWithProgress((pn) =>
        /// {
        ///    var result = new List&lt;int&gt;();
        ///    for (int i = 0; i &lt; 100000; i++)
        ///    {
        ///        ...
        ///        result.Add(i);
        ///        pn.CancellationToken.ThrowIfCancellationRequested();
        ///        pn.Notify(i * 10);
        ///    }
        ///    return result.ToArray();
        /// },
        ///    // Progress Handling
        /// (progressPercentage) =>
        /// {
        ///    var threadID = Thread.CurrentThread.ManagedThreadId;
        ///    Console.WriteLine("{0} - {1}", progressPercentage, threadID);
        ///    _threadOfExecution.Add(threadID);
        /// }, tokenSource.Token);
        /// </code>
        /// </example>
        /// <param name="factory"><see cref="TaskFactory"/> instance to be extended.</param>
        /// <param name="function">function to execute async</param>
        /// <param name="progressHandler">handler that gets called on the caller's thread (e.g. UI thread) and where progress can be reported back to the 
        /// user.</param>
        /// <param name="cancellationToken">token that can be used inside the async function to abort the operation.</param>
        /// <returns><see cref="Task"/> that can be awaited</returns>
        public static Task StartNewWithProgress(this TaskFactory factory,
                                                     Action<IProgressNotifier> function,
                                                     Action<int> progressHandler,
                                                     CancellationToken cancellationToken)
        {
            var notifier = new ProgressNotifier(cancellationToken)
            {
                ProgressHandler = progressHandler
            };
            return factory.StartNew((p) => function(notifier), cancellationToken);
        }
    }
}